001 /*
002 * Copyright 2003-2005 The Apache Software Foundation
003 * Copyright 2005 Stephen McConnell
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package net.dpml.cli.commandline;
018
019 import java.io.IOException;
020
021 import java.util.LinkedList;
022 import java.util.List;
023 import java.util.ListIterator;
024
025 import net.dpml.cli.CommandLine;
026 import net.dpml.cli.Group;
027 import net.dpml.cli.Option;
028 import net.dpml.cli.OptionException;
029 import net.dpml.cli.WriteableCommandLine;
030 import net.dpml.cli.resource.ResourceConstants;
031 import net.dpml.cli.util.HelpFormatter;
032
033 /**
034 * A class that implements the <code>Parser</code> interface can parse a
035 * String array according to the {@link Group}specified and return a
036 * {@link CommandLine}.
037 *
038 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
039 * @version 1.0.0
040 */
041 public class Parser
042 {
043 private HelpFormatter m_helpFormatter = new HelpFormatter();
044 private Option m_helpOption = null;
045 private String m_helpTrigger = null;
046 private Group m_group = null;
047
048 /**
049 * Parse the arguments according to the specified options and properties.
050 *
051 * @param arguments the command line arguments
052 * @return the list of atomic option and value tokens
053 * @throws OptionException if there are any problems encountered while parsing the
054 * command line tokens.
055 */
056 public CommandLine parse( final String[] arguments ) throws OptionException
057 {
058 // build a mutable list for the arguments
059 final List argumentList = new LinkedList();
060
061 // copy the arguments into the new list
062 for( int i = 0; i < arguments.length; i++ )
063 {
064 final String argument = arguments[i];
065
066 // ensure non intern'd strings are used
067 // so that == comparisons work as expected
068 argumentList.add( new String( argument ) );
069 }
070
071 // wet up a command line for this group
072 final WriteableCommandLine commandLine =
073 new WriteableCommandLineImpl( m_group, argumentList );
074
075 // pick up any defaults from the model
076 m_group.defaults( commandLine );
077
078 // process the options as far as possible
079 final ListIterator iterator = argumentList.listIterator();
080 Object previous = null;
081
082 while( m_group.canProcess( commandLine, iterator ) )
083 {
084 // peek at the next item and backtrack
085 final Object next = iterator.next();
086 iterator.previous();
087 // if we have just tried to process this instance
088 if( next == previous )
089 {
090 // abort
091 break;
092 }
093 // remember previous
094 previous = next;
095 m_group.process( commandLine, iterator );
096 }
097
098 // if there are more arguments we have a problem
099 if( iterator.hasNext() )
100 {
101 final String arg = (String) iterator.next();
102 throw new OptionException(
103 m_group,
104 ResourceConstants.UNEXPECTED_TOKEN,
105 arg );
106 }
107
108 // no need to validate if the help option is present
109 if( !commandLine.hasOption( m_helpOption ) && !commandLine.hasOption( m_helpTrigger ) )
110 {
111 m_group.validate( commandLine );
112 }
113 return commandLine;
114 }
115
116 /**
117 * Parse the arguments according to the specified options and properties and
118 * displays the usage screen if the CommandLine is not valid or the help
119 * option was specified.
120 *
121 * @param arguments the command line arguments
122 * @return a valid CommandLine or null if the parse was unsuccessful
123 * @throws IOException if an error occurs while formatting help
124 */
125 public CommandLine parseAndHelp( final String[] arguments ) throws IOException
126 {
127 m_helpFormatter.setGroup( m_group );
128
129 try
130 {
131 // attempt to parse the command line
132 final CommandLine commandLine = parse( arguments );
133 if( !commandLine.hasOption( m_helpOption ) && !commandLine.hasOption( m_helpTrigger ) )
134 {
135 return commandLine;
136 }
137 }
138 catch( final OptionException oe )
139 {
140 // display help regarding the exception
141 m_helpFormatter.setException( oe );
142 }
143
144 // print help
145 m_helpFormatter.print();
146 return null;
147 }
148
149 /**
150 * Sets the Group of options to parse against
151 * @param group the group of options to parse against
152 */
153 public void setGroup( final Group group )
154 {
155 m_group = group;
156 }
157
158 /**
159 * Sets the HelpFormatter to use with the simplified parsing.
160 * @see #parseAndHelp(String[])
161 * @param helpFormatter the HelpFormatter to use with the simplified parsing
162 */
163 public void setHelpFormatter( final HelpFormatter helpFormatter )
164 {
165 m_helpFormatter = helpFormatter;
166 }
167
168 /**
169 * Sets the help option to use with the simplified parsing. For example
170 * <code>--help</code>, <code>-h</code> and <code>-?</code> are often used.
171 * @see #parseAndHelp(String[])
172 * @param helpOption the help Option
173 */
174 public void setHelpOption( final Option helpOption )
175 {
176 m_helpOption = helpOption;
177 }
178
179 /**
180 * Sets the help option to use with the simplified parsing. For example
181 * <code>--help</code>, <code>-h</code> and <code>-?</code> are often used.
182 * @see #parseAndHelp(String[])
183 * @param helpTrigger the trigger of the help Option
184 */
185 public void setHelpTrigger( final String helpTrigger )
186 {
187 m_helpTrigger = helpTrigger;
188 }
189 }